{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5a96017a",
   "metadata": {},
   "source": [
    "# \"THE PRICE IS RIGHT\" Capstone Project\n",
    "\n",
    "This week - build a model that predicts how much something costs from a description, based on a scrape of Amazon data\n",
    "\n",
    "\n",
    "A model that can estimate how much something costs, from its description.\n",
    "\n",
    "# Order of play\n",
    "\n",
    "DAY 1: Data Curation  \n",
    "DAY 2: Data Pre-processing  \n",
    "DAY 3: Evaluation, Baselines, Traditional ML  \n",
    "DAY 4: Deep Learning and LLMs  \n",
    "DAY 5: Fine-tuning a Frontier Model  \n",
    "\n",
    "## DAY 2: Data Pre-processing\n",
    "\n",
    "Today we'll rewrite the products into a standard format.  \n",
    "LLMs are great at this!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "277ab97c",
   "metadata": {},
   "source": [
    "<table style=\"margin: 0; text-align: left;\">\n",
    "    <tr>\n",
    "        <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
    "            <img src=\"../assets/business.jpg\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
    "        </td>\n",
    "        <td>\n",
    "            <h2 style=\"color:#181;\">Business value of Data Pre-processing / Re-writing</h2>\n",
    "            <span style=\"color:#181;\">LLMs have made it simple to do something that was considered impossible only a few years ago.\n",
    "            This approach can be applied to almost any business vertical, and it's similar to the advanced techniques\n",
    "            we used on Week 5.</span>\n",
    "        </td>\n",
    "    </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0f562f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from litellm import completion\n",
    "from dotenv import load_dotenv\n",
    "import json\n",
    "from pricer.batch import Batch\n",
    "from pricer.items import Item\n",
    "\n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7a59cb4",
   "metadata": {},
   "source": [
    "# The next cell is where you choose Dataset\n",
    "\n",
    "Use `LITE_MODE = True` for the free, fast version with training data size of 20,000\n",
    "\n",
    "USe `LITE_MODE =  False` for the powerful, full version with training data size of 800,000\n",
    "\n",
    "## For this lab\n",
    "\n",
    "You can skip altogether and load the dataset from HuggingFace: $0\n",
    "\n",
    "You can run pre-processing for the lite dataset: under $1\n",
    "\n",
    "You can run pre-processing for the full dataset: $30"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3f5c1ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "LITE_MODE = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ede29b14",
   "metadata": {},
   "outputs": [],
   "source": [
    "username = \"ed-donner\"\n",
    "dataset = f\"{username}/items_raw_lite\" if LITE_MODE else f\"{username}/items_raw_full\"\n",
    "\n",
    "train, val, test = Item.from_hub(dataset)\n",
    "\n",
    "items = train + val + test\n",
    "\n",
    "print(f\"Loaded {len(items):,} items\")\n",
    "print(items[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5e3b0a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "items[2].id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37158a12",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Give every item an id\n",
    "\n",
    "for index, item in enumerate(items):\n",
    "    item.id = index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8651446b",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "SYSTEM_PROMPT = \"\"\"Create a concise description of a product. Respond only in this format. Do not include part numbers.\n",
    "Title: Rewritten short precise title\n",
    "Category: eg Electronics\n",
    "Brand: Brand name\n",
    "Description: 1 sentence description\n",
    "Details: 1 sentence on features\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ad12c20",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(items[0].full)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae5e65d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = [{\"role\": \"system\", \"content\": SYSTEM_PROMPT}, {\"role\": \"user\", \"content\": items[0].full}]\n",
    "response = completion(messages=messages, model=\"groq/openai/gpt-oss-20b\", reasoning_effort=\"low\")\n",
    "\n",
    "print(response.choices[0].message.content)\n",
    "print()\n",
    "print(f\"Input tokens: {response.usage.prompt_tokens}\")\n",
    "print(f\"Output tokens: {response.usage.completion_tokens}\")\n",
    "print(f\"Cost: {response._hidden_params['response_cost']*100:.3f} cents\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9ecb10a9",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "messages = [{\"role\": \"system\", \"content\": SYSTEM_PROMPT}, {\"role\": \"user\", \"content\": items[0].full}]\n",
    "response = completion(messages=messages, model=\"ollama/llama3.2\", api_base=\"http://localhost:11434\")\n",
    "print(response.choices[0].message.content)\n",
    "print()\n",
    "print(f\"Input tokens: {response.usage.prompt_tokens}\")\n",
    "print(f\"Output tokens: {response.usage.completion_tokens}\")\n",
    "print(f\"Cost: {response._hidden_params['response_cost']*100:.3f} cents\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4749a09",
   "metadata": {},
   "outputs": [],
   "source": [
    "MODEL = \"openai/gpt-oss-20b\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b99d1d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_jsonl(item):\n",
    "    body = {\"model\": MODEL, \"messages\": [{\"role\": \"system\", \"content\": SYSTEM_PROMPT}, {\"role\": \"user\", \"content\": item.full}], \"reasoning_effort\": \"low\"}\n",
    "    line = {\"custom_id\": str(item.id), \"method\": \"POST\", \"url\": \"/v1/chat/completions\", \"body\": body}\n",
    "    return json.dumps(line)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66d49d6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "items[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a905afa6",
   "metadata": {},
   "outputs": [],
   "source": [
    "make_jsonl(items[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7d3e608",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def make_file(start, end, filename):\n",
    "    batch_file = filename\n",
    "    with open(batch_file, \"w\") as f:\n",
    "        for i in range(start, end):\n",
    "            f.write(make_jsonl(items[i]))\n",
    "            f.write(\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03fd4522",
   "metadata": {},
   "outputs": [],
   "source": [
    "make_file(0, 1000, \"jsonl/0_1000.jsonl\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "346babf7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from groq import Groq\n",
    "\n",
    "groq = Groq(api_key=os.environ.get(\"GROQ_API_KEY\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5bf887de",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "with open(\"jsonl/0_1000.jsonl\", \"rb\") as f:\n",
    "    response = groq.files.create(file=f, purpose=\"batch\")\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be59f84c",
   "metadata": {},
   "outputs": [],
   "source": [
    "file_id = response.id\n",
    "file_id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aa7a4742",
   "metadata": {},
   "outputs": [],
   "source": [
    "response = groq.batches.create(completion_window=\"24h\", endpoint=\"/v1/chat/completions\", input_file_id=file_id)\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bdedac18",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = groq.batches.retrieve(response.id)\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "747a2de7",
   "metadata": {},
   "outputs": [],
   "source": [
    "response = groq.files.content(result.output_file_id)\n",
    "response.write_to_file(\"jsonl/batch_results.jsonl\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63094046",
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(\"jsonl/batch_results.jsonl\", \"r\") as f:\n",
    "    for line in f:\n",
    "        json_line = json.loads(line)\n",
    "        id = int(json_line[\"custom_id\"])\n",
    "        summary = json_line[\"response\"][\"body\"][\"choices\"][0][\"message\"][\"content\"]\n",
    "        items[id].summary = summary\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1974a5c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(items[0].full)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d6d2b213",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(items[1000].summary)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d708491a",
   "metadata": {},
   "source": [
    "## I've put exactly this logic into a Batch class\n",
    "\n",
    "- Divides items into groups of 1,000\n",
    "- Kicks off batches for each\n",
    "- Allows us to monitor and collect the results when complete\n",
    "\n",
    "## COSTS\n",
    "\n",
    "Using Groq, for me - this cost under $1 for the Lite dataset and under $30 for the big dataset\n",
    "\n",
    "But you don't need to pay anything! In the next lab, you can load my pre-processed results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "921381c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "Batch.create(items, LITE_MODE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea268775",
   "metadata": {},
   "outputs": [],
   "source": [
    "Batch.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1dd697a",
   "metadata": {},
   "outputs": [],
   "source": [
    "Batch.fetch()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb8f6107",
   "metadata": {},
   "outputs": [],
   "source": [
    "for index, item in enumerate(items):\n",
    "    if not item.summary:\n",
    "        print(index)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "230d86e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(items[10234].summary)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ddbbc275",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Remove the fields that we don't need in the hub\n",
    "\n",
    "for item in items:\n",
    "    item.full = None\n",
    "    item.id = None"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20533d23",
   "metadata": {},
   "source": [
    "## Push the final dataset to the hub\n",
    "\n",
    "If lite mode, we'll only push the lite dataset\n",
    "\n",
    "If full mode, we'll push both datasets (in case you decide to use lite later)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6335630c",
   "metadata": {},
   "outputs": [],
   "source": [
    "username = \"ed-donner\"\n",
    "full = f\"{username}/items_full\"\n",
    "lite = f\"{username}/items_lite\"\n",
    "\n",
    "if LITE_MODE:\n",
    "    train = items[:20_000]\n",
    "    val = items[20_000:21_000]\n",
    "    test = items[21_000:]\n",
    "    Item.push_to_hub(lite, train, val, test)\n",
    "else:\n",
    "    train = items[:800_000]\n",
    "    val = items[800_000:810_000]\n",
    "    test = items[810_000:]\n",
    "    Item.push_to_hub(full, train, val, test)\n",
    "\n",
    "    train_lite = train[:20_000]\n",
    "    val_lite = val[:1_000]\n",
    "    test_lite = test[:1_000]\n",
    "    Item.push_to_hub(lite, train_lite, val_lite, test_lite)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7bb25f4",
   "metadata": {},
   "source": [
    "## And here they are!\n",
    "\n",
    "https://huggingface.co/datasets/ed-donner/items_lite\n",
    "\n",
    "https://huggingface.co/datasets/ed-donner/items_full\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "033ad0d8",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
